package com.art.coconut.core;

import com.art.coconut.change.BaseChangePojo;
import com.art.coconut.change.ChangePojo;
import com.art.coconut.change.FinalChangePojo;
import com.art.coconut.enums.ChangeTypeEnum;
import com.art.coconut.enums.ExceptionEnum;
import com.art.coconut.exception.CoconutCommonException;
import com.art.coconut.utils.CoconutHelper;
import com.art.coconut.utils.ProcessHandle;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class Coconut {
    public static int depth = 5;
    public static List<ChangePojo> compareCollectionWithDepth(Collection<?> oldVersion, Collection<?> currentVersion, int depth) throws IllegalAccessException, InstantiationException {
        if(depth > Coconut.depth){
            throw new CoconutCommonException(ExceptionEnum.OVER_DEPTH_EXCEPTION.getCode(), ExceptionEnum.OVER_DEPTH_EXCEPTION.getMsg());
        }
        List<ChangePojo> changePojoList = new ArrayList<>();
        Class<?> itemClass;
        if(!oldVersion.isEmpty()){
            itemClass = ((List<?>)oldVersion).get(0).getClass();
        }else if(!currentVersion.isEmpty()){
            itemClass = ((List<?>)currentVersion).get(0).getClass();
        }else {
            return changePojoList;
        }
        if(!oldVersion.isEmpty() && !currentVersion.isEmpty()){
            Class<?> c1 = ((List<?>)oldVersion).get(0).getClass();
            Class<?> c2 = ((List<?>)currentVersion).get(0).getClass();
            if(!c1.getTypeName().equals(c2.getTypeName())){
                throw new CoconutCommonException(ExceptionEnum.INCONSISTENT_CLASS_TYPE_EXCEPTION.getCode()
                        , ExceptionEnum.INCONSISTENT_CLASS_TYPE_EXCEPTION.getMsg());
            }
        }
        Map<Object, Object> oldVersionMap = ProcessHandle.collectionToMap(oldVersion);
        Map<Object, Object> currVersionMap = ProcessHandle.collectionToMap(currentVersion);
        // 删除, curr没有old的元素
        CoconutHelper.processVersionMapAddAndRemove(changePojoList, currVersionMap, oldVersionMap
                , ChangeTypeEnum.REMOVED, itemClass, depth);
        // 新增 old中没有curr元素
        CoconutHelper.processVersionMapAddAndRemove(changePojoList, oldVersionMap, currVersionMap
                , ChangeTypeEnum.ADDED, itemClass, depth);
        // 修改
        CoconutHelper.processVersionMapModify(changePojoList, oldVersionMap, currVersionMap
                , ChangeTypeEnum.MODIFIED, itemClass, depth);
        return changePojoList;
    }
    public static List<ChangePojo> compareCollection(Collection<?> oldVersion, Collection<?> currentVersion) throws InstantiationException, IllegalAccessException {
        return compareCollectionWithDepth(oldVersion, currentVersion, 1);
    }
    public static List<BaseChangePojo> processCollection(Collection<?> oldVersion, Collection<?> currentVersion) throws IllegalAccessException, InstantiationException {
        List<BaseChangePojo> baseChangePojoList = new ArrayList<>();
        List<ChangePojo> changePojoList = compareCollection(oldVersion, currentVersion);
        CoconutHelper.getChangeList(baseChangePojoList, changePojoList, null);
        return baseChangePojoList;
    }
    public static <T>List<BaseChangePojo> processObject(T oldVersion, T currentVersion) throws InstantiationException, IllegalAccessException {
        List<T> oldList = Arrays.asList(oldVersion);
        List<T> currList = Arrays.asList(currentVersion);
        return processCollection(oldList, currList);
    }
    public static List<FinalChangePojo> getFinaChangeList(String parentId
            , String entityTag
            , List<BaseChangePojo> baseChangePojoList
            , boolean isFull){
        List<FinalChangePojo> finalChangePojoList = new ArrayList<>();
        List<BaseChangePojo> filterObject;
        if(isFull){
            filterObject = baseChangePojoList
                    .stream()
                    .filter(b-> b.getEntityTag().equals(entityTag)
                            && b.getCollectionFlag()
                            && ((StringUtils.isBlank(parentId) && StringUtils.isBlank(b.getParentId())) || (!Objects.isNull(b.getParentId()) && b.getParentId().equals(parentId))))
                    .collect(Collectors.toList());
        }else {
            filterObject = baseChangePojoList
                    .stream()
                    .filter(b-> b.getEntityTag().equals(entityTag)
                            && b.getCollectionFlag()
                            && ((StringUtils.isBlank(parentId) && StringUtils.isBlank(b.getParentId())) || (!Objects.isNull(b.getParentId()) && b.getParentId().equals(parentId)))
                            && !b.getChangeType().equals(ChangeTypeEnum.UNCHANGED))
                    .collect(Collectors.toList());
        }
        filterObject.forEach(f->{
            FinalChangePojo finalChangePojo = new FinalChangePojo();
            finalChangePojo.setChangeType(f.getChangeType());
            if(f.getChangeType().equals(ChangeTypeEnum.ADDED)){
                finalChangePojo.setObject(f.getAfter());
            }else if(f.getChangeType().equals(ChangeTypeEnum.REMOVED)){
                finalChangePojo.setObject(f.getBefore());
            }else {
                finalChangePojo.setObject(f.getAfter());
            }
            finalChangePojoList.add(finalChangePojo);
        });
        return finalChangePojoList;
    }
    public static List<BaseChangePojo> getFinalChangeProperty(String parentId
            , String entityTag
            , List<BaseChangePojo> baseChangePojoList
            , boolean isFull){
        if(isFull){
            return baseChangePojoList.stream().filter(b->
                b.getEntityTag().equals(entityTag)
                        && !b.getCollectionFlag()
                        && ((StringUtils.isBlank(parentId) && StringUtils.isBlank(b.getParentId())) || (!Objects.isNull(b.getParentId()) && b.getParentId().equals(parentId)))
            ).collect(Collectors.toList());
        }else{
            return baseChangePojoList.stream().filter(b->
                b.getEntityTag().equals(entityTag)
                        && !b.getCollectionFlag()
                        && ((StringUtils.isBlank(parentId) && StringUtils.isBlank(b.getParentId())) || (!Objects.isNull(b.getParentId()) && b.getParentId().equals(parentId)))
                        && !b.getChangeType().equals(ChangeTypeEnum.UNCHANGED)
            ).collect(Collectors.toList());
        }
    }

    public static void prettyPrint(List<BaseChangePojo> baseChangePojoList, boolean isFull){
        if (baseChangePojoList==null) return;
        List<BaseChangePojo> processList;
        if(isFull){
           processList = new ArrayList<>(baseChangePojoList);
        }else {
            processList = baseChangePojoList
                    .stream()
                    .filter(b->!b.getChangeType().equals(ChangeTypeEnum.UNCHANGED))
                    .collect(Collectors.toList());
        }
        List<BaseChangePojo> parent = processList
                .stream()
                .filter(p->p.getCollectionFlag() && StringUtils.isBlank(p.getParentId()))
                .collect(Collectors.toList());
        print(processList, parent, "");
    }
    private static void print(List<BaseChangePojo> baseChangePojoList,List<BaseChangePojo> parentChangePojoList, String splitLine){
        for (BaseChangePojo changePojo : parentChangePojoList) {
            log.info("{}EntityTag:{}/{}  {}", splitLine, changePojo.getEntityTag(), changePojo.getTypeId(), changePojo.getChangeType().getDesc());
            List<BaseChangePojo> subPropertyChange = baseChangePojoList
                    .stream()
                    .filter(b->(!Objects.isNull(b.getParentId()) && b.getParentId().equals(changePojo.getTypeId()))
                            && !b.getCollectionFlag()
                    )
                    .collect(Collectors.toList());
            for (BaseChangePojo pojo : subPropertyChange) {
                log.info("{}{}:{}==>{}  {}", splitLine+"--", pojo.getTypeName(), pojo.getBefore(), pojo.getAfter(), pojo.getChangeType().getDesc());
            }
            List<BaseChangePojo> subChangeList = baseChangePojoList
                    .stream()
                    .filter(b->(!Objects.isNull(b.getParentId()) && b.getParentId().equals(changePojo.getTypeId()))
                            && b.getCollectionFlag()
                    )
                    .collect(Collectors.toList());
            print(baseChangePojoList, subChangeList, splitLine+"--");
        }
    }
}
